﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading.Tasks;
using System.Xml.Serialization;
using AdRotator.Model;
using Tools;
using Windows.Storage;
using Windows.UI.Xaml;
using Windows.UI.Xaml.Controls;
using Windows.UI.Xaml.Media.Animation;
using Windows.Networking.Connectivity;
using InMobi.W8.AdSDK;

// The Templated Control item template is documented at http://go.microsoft.com/fwlink/?LinkId=234235

namespace AdRotatorWin8
{
    public sealed class AdRotatorControl : Control, IDisposable
    {
        private bool _loaded = false;

        private bool _adsAvailable = false;

        private bool _initialised = false;

        private bool _slidingAdHidden = false;

        private bool _slidingAdTimerStarted = false;

        public delegate void LogHandler(string message);

        public event LogHandler Log;

        private AdType CurrentAdType;

        private AdCultureDescriptor CurrentCulture = null;

        private const string SETTINGS_FILE_NAME = "AdRotatorSettings";

        public bool IsInDesignMode
        {
            get
            {
                return Windows.ApplicationModel.DesignMode.DesignModeEnabled;
            }
        }

        private Grid LayoutRoot
        {
            get
            {
                return GetTemplateChild("LayoutRoot") as Grid;
            }
        }

        /// <summary>
        /// The displayed ad control instance
        /// </summary>
        private object _currentAdControl;

        /// <summary>
        /// Random generato
        /// </summary>
        private static Random _rnd = new Random();

        /// <summary>
        /// List of the ad types that have failed to load
        /// </summary>
        private static List<AdType> _failedAdTypes = new List<AdType>();

        /// <summary>
        /// The ad settings based on which the ad descriptor for the current UI culture can be selected
        /// </summary>
        private static AdSettings _settings;

        /// <summary>
        /// Indicates whether there has been an attemt to fetch the remote settings file
        /// </summary>
        private static bool _remoteAdSettingsFetched = false;

        /// <summary>
        /// Local variable to allow other components of AdRotator to access settings without UI Thread
        /// </summary>
        private string _settingsURL;

        /// <summary>
        /// Indicates whether a network has been detected and available, is turned off if none found
        /// </summary>
        public static bool IsNetworkAvailable { get; private set; }


        #region LoggingEventCode
        private void OnLog(string message)
        {
            if (Log != null)
            {
                Log(message);
            }
        }
        #endregion

        #region SettingsUrl

        /// <summary>
        /// Gets or sets the URL of the remote ad descriptor file
        /// </summary>
        public string SettingsUrl
        {
            get { return (string)GetValue(SettingsUrlProperty); }
            set { SetValue(SettingsUrlProperty, value); _settingsURL = value.ToString(); }
        }

        public static readonly DependencyProperty SettingsUrlProperty = DependencyProperty.Register("SettingsUrl", typeof(string), typeof(AdRotatorControl), new PropertyMetadata(""));

        //Force Loading Settings Manually
        //private static void SettingsUrlChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        //{
        //    var sender = d as AdRotatorControl;
        //    if(sender != null)
        //    {
        //        sender.OnSettingsUrlChanged(e);
        //    }
        //}

        //private void OnSettingsUrlChanged(DependencyPropertyChangedEventArgs e)
        //{
        //    if (!IsInDesignMode)
        //    {
        //        FetchAdSettingsThreaded();
        //    }
        //}

        #endregion

        #region DefaultSettingsFileUri

        public string DefaultSettingsFileUri
        {
            get { return (string)GetValue(DefaultSettingsFileUriProperty); }
            set { SetValue(DefaultSettingsFileUriProperty, value); }
        }

        public static readonly DependencyProperty DefaultSettingsFileUriProperty = DependencyProperty.Register("DefaultSettingsFileUri", typeof(string), typeof(AdRotatorControl), new PropertyMetadata("", DefaultSettingsFileUriChanged));

        private static void DefaultSettingsFileUriChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sender = d as AdRotatorControl;
            if (sender != null)
            {
                sender.OnDefaultSettingsFileUriChanged(e);
            }
        }

        private async void OnDefaultSettingsFileUriChanged(DependencyPropertyChangedEventArgs e)
        {
            if (!_initialised && string.IsNullOrEmpty(SettingsUrl))
            {
                if (_loaded)
                {
                    LoadAdSettings();
                    await Invalidate();
                }

            }
        }

        #endregion

        #region DefaultAdType

        public AdType DefaultAdType
        {
            get { return (AdType)GetValue(DefaultAdTypeProperty); }
            set { SetValue(DefaultAdTypeProperty, value); }
        }

        public static readonly DependencyProperty DefaultAdTypeProperty = DependencyProperty.Register("DefaultAdType", typeof(AdType), typeof(AdRotatorControl), new PropertyMetadata(AdType.None));

        #endregion

        #region IsEnabled

        /// <summary>
        /// When set to false the control does not display
        /// </summary>
        public bool IsAdRotatorEnabled
        {
            get { return (bool)GetValue(IsEnabledProperty); }
            set { SetValue(IsEnabledProperty, value); }
        }

        public static readonly DependencyProperty IsAdRotatorEnabledProperty = DependencyProperty.Register("IsAdRotatorEnabled", typeof(bool), typeof(AdRotatorControl), new PropertyMetadata(true, IsAdRotatorEnabledChanged));

        private static void IsAdRotatorEnabledChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sender = d as AdRotatorControl;
            if (sender != null)
            {
                sender.OnIsEnabledChangedChanged(e);
            }
        }

        private async void OnIsEnabledChangedChanged(DependencyPropertyChangedEventArgs e)
        {
            if (_initialised)
            {
                await Invalidate();
            }
        }

        #endregion

        #region IsInitialised

        public bool IsInitialised
        {
            get { return _initialised; }
        }

        #endregion

        #region SlidingAd Properties

        Storyboard SlidingAdTimer;
        Storyboard SlideOutLRAdStoryboard;
        Storyboard SlideInLRAdStoryboard;
        Storyboard SlideOutUDAdStoryboard;
        Storyboard SlideInUDAdStoryboard;

        #region SlidingAdDirection

        /// <summary>
        /// Direction the popup will hide / appear from
        /// If not set the AdControl will remain on screen
        /// </summary>
        public SlideDirection SlidingAdDirection
        {
            get { return (SlideDirection)GetValue(SlidingAdDirectionProperty); }
            set { SetValue(SlidingAdDirectionProperty, value); }
        }

        public static readonly DependencyProperty SlidingAdDirectionProperty = DependencyProperty.Register("SlidingAdDirection", typeof(SlideDirection), typeof(AdRotatorControl), new PropertyMetadata(SlideDirection.None, SlidingAdDirectionChanged));

        private static void SlidingAdDirectionChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sender = d as AdRotatorControl;
            if (sender != null)
            {
                sender.OnSlidingAdDirectionChanged(e);
            }
        }

        private void OnSlidingAdDirectionChanged(DependencyPropertyChangedEventArgs e)
        {
            if (LayoutRoot != null)
            {
                var bounds = Window.Current.Bounds;

                switch ((SlideDirection)e.NewValue)
                {
                    case SlideDirection.Left:
                        ((DoubleAnimation)SlideOutLRAdStoryboard.Children[0]).To = -(bounds.Width * 2);
                        ((DoubleAnimation)SlideInLRAdStoryboard.Children[0]).From = -(bounds.Width * 2);
                        break;
                    case SlideDirection.Right:
                        ((DoubleAnimation)SlideOutLRAdStoryboard.Children[0]).To = bounds.Width * 2;
                        ((DoubleAnimation)SlideInLRAdStoryboard.Children[0]).From = bounds.Width * 2;
                        break;
                    case SlideDirection.Bottom:
                        ((DoubleAnimation)SlideOutUDAdStoryboard.Children[0]).To = bounds.Height * 2;
                        ((DoubleAnimation)SlideInUDAdStoryboard.Children[0]).From = bounds.Height * 2;
                        break;
                    case SlideDirection.Top:
                        ((DoubleAnimation)SlideOutUDAdStoryboard.Children[0]).To = -(bounds.Height * 2);
                        ((DoubleAnimation)SlideInUDAdStoryboard.Children[0]).From = -(bounds.Height * 2);
                        break;
                    default:
                        ((DoubleAnimation)SlideOutLRAdStoryboard.Children[0]).To = 0;
                        ((DoubleAnimation)SlideInLRAdStoryboard.Children[0]).From = 0;
                        ((DoubleAnimation)SlideOutUDAdStoryboard.Children[0]).To = 0;
                        ((DoubleAnimation)SlideInUDAdStoryboard.Children[0]).From = 0;
                        break;
                }
            }
        }


        #endregion

        #region SlidingAdDisplaySeconds

        /// <summary>
        /// Amount of time in seconds the ad is displayed on Screen if <see cref="SlidingAdDirection"/> is set to something else than None
        /// </summary>
        public int SlidingAdDisplaySeconds
        {
            get { return (int)GetValue(SlidingAdDisplaySecondsProperty); }
            set { SetValue(SlidingAdDisplaySecondsProperty, value); }
        }

        public static readonly DependencyProperty SlidingAdDisplaySecondsProperty = DependencyProperty.Register("SlidingAdDisplaySeconds", typeof(int), typeof(AdRotatorControl), new PropertyMetadata(10));

        #endregion

        #region SlidingAdHiddenSeconds

        /// <summary>
        ///  Amount of time in seconds to wait before displaying the ad again 
        ///  (if <see cref="SlidingAdDirection"/> is set to something else than None).
        ///  Basically the lower this number the more the user is "nagged" by the ad coming back now and again
        /// </summary>
        public int SlidingAdHiddenSeconds
        {
            get { return (int)GetValue(SlidingAdHiddenSecondsProperty); }
            set { SetValue(SlidingAdHiddenSecondsProperty, value); }
        }

        public static readonly DependencyProperty SlidingAdHiddenSecondsProperty = DependencyProperty.Register("SlidingAdHiddenSeconds", typeof(int), typeof(AdRotatorControl), new PropertyMetadata(20));

        #endregion

        #endregion

        #region ADProviderProperties

        #region DefaultHouseAd

        #region DefaultHouseAdBody

        public string DefaultHouseAdBody
        {
            get
            {
                if (string.IsNullOrEmpty((string)GetValue(DefaultHouseAdBodyProperty)))
                {
                    SetValue(DefaultHouseAdBodyProperty, GetSecondaryID(AdType.DefaultHouseAd));
                }
                return (string)GetValue(DefaultHouseAdBodyProperty);
            }
            set { SetValue(DefaultHouseAdBodyProperty, value); }
        }

        // Using a DependencyProperty as the backing store for DefaultHouseAdBody.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty DefaultHouseAdBodyProperty =
            DependencyProperty.Register("DefaultHouseAdBody", typeof(string), typeof(AdRotatorControl), new PropertyMetadata(string.Empty));
        #endregion

        #region DefaultHouseAdURI

        public string DefaultHouseAdURI
        {
            get
            {
                if (string.IsNullOrEmpty((string)GetValue(DefaultHouseAdURIProperty)))
                {
                    SetValue(DefaultHouseAdURIProperty, GetAppID(AdType.DefaultHouseAd));
                }
                return (string)GetValue(DefaultHouseAdURIProperty);
            }
            set { SetValue(DefaultHouseAdURIProperty, value); }
        }

        public static readonly DependencyProperty DefaultHouseAdURIProperty = DependencyProperty.Register("DefaultHouseAdURI", typeof(string), typeof(AdRotatorControl), new PropertyMetadata(String.Empty));

        #endregion

        public delegate void DefaultHouseAdClickEventHandler();

        public event DefaultHouseAdClickEventHandler DefaultHouseAdClick;
        #endregion

        #region Pubcenter
        #region PubCenterAppId

        public string PubCenterAppId
        {
            get
            {
                if (string.IsNullOrEmpty((string)GetValue(PubCenterAppIdProperty)))
                {
                    SetValue(PubCenterAppIdProperty, GetAppID(AdType.PubCenter));
                }
                return (string)GetValue(PubCenterAppIdProperty);
            }
            set { SetValue(PubCenterAppIdProperty, value); }
        }

        public static readonly DependencyProperty PubCenterAppIdProperty = DependencyProperty.Register("PubCenterAppId", typeof(string), typeof(AdRotatorControl), new PropertyMetadata(String.Empty));

        #endregion

        #region PubCenterAdUnitId

        public string PubCenterAdUnitId
        {
            get
            {
                if (string.IsNullOrEmpty((string)GetValue(PubCenterAdUnitIdProperty)))
                {
                    SetValue(PubCenterAdUnitIdProperty, GetSecondaryID(AdType.PubCenter));
                }
                return (string)GetValue(PubCenterAdUnitIdProperty);
            }
            set { SetValue(PubCenterAdUnitIdProperty, value); }
        }

        public static readonly DependencyProperty PubCenterAdUnitIdProperty = DependencyProperty.Register("PubCenterAdUnitId", typeof(string), typeof(AdRotatorControl), new PropertyMetadata(""));

        #endregion
        #endregion

        #region AdDuplexAppId

        public string AdDuplexAppId
        {
            get
            {
                if (string.IsNullOrEmpty((string)GetValue(AdDuplexAppIdProperty)))
                {
                    SetValue(AdDuplexAppIdProperty, GetAppID(AdType.AdDuplex));
                }
                return (string)GetValue(AdDuplexAppIdProperty);
            }
            set { SetValue(AdDuplexAppIdProperty, value); }
        }

        public static readonly DependencyProperty AdDuplexAppIdProperty = DependencyProperty.Register("AdDuplexAppId", typeof(string), typeof(AdRotatorControl), new PropertyMetadata(""));

        #endregion

        #region InMobiAppId

        public string InMobiAppId
        {
            get
            {
                if (string.IsNullOrEmpty((string)GetValue(InMobiAppIdProperty)))
                {
                    SetValue(InMobiAppIdProperty, GetAppID(AdType.InMobi));
                }
                return (string)GetValue(InMobiAppIdProperty);
            }
            set { SetValue(InMobiAppIdProperty, value); }
        }

        public static readonly DependencyProperty InMobiAppIdProperty = DependencyProperty.Register("InMobiAppId", typeof(string), typeof(AdRotatorControl), new PropertyMetadata(""));

        #endregion

        #region AdValidSettings

        private bool IsDefaultHouseAdValid
        {
            get
            {
                return DefaultHouseAdBody != null || !string.IsNullOrEmpty(DefaultHouseAdURI);
            }
        }

        private bool IsPubCenterValid
        {
            get
            {
                return !String.IsNullOrEmpty(PubCenterAppId) && !String.IsNullOrEmpty(PubCenterAdUnitId);
            }
        }

        private bool IsAdDuplexValid
        {
            get
            {
                return !String.IsNullOrEmpty(AdDuplexAppId);
            }
        }

        private bool IsInMobiValid
        {
            get
            {
                return !String.IsNullOrEmpty(InMobiAppId);
            }
        }

        private bool IsAdTypeValid(AdType adType)
        {
            switch (adType)
            {
                case AdType.PubCenter:
                    return IsPubCenterValid;
                case AdType.AdDuplex:
                    return IsAdDuplexValid;
                case AdType.InMobi:
                    return IsInMobiValid;
                case AdType.DefaultHouseAd:
                    return IsDefaultHouseAdValid;

            }
            //Davide Cleopadre www.cleosolutions.com
            //if for any reason the AdType cannot be found is not valid
            //if we add new ads type the control will continue to work
            //also not updated
            return false;
        }

        #endregion

        #endregion

        #region AdWidth

        /// <summary>
        /// Sets the Ad Controls Ad Width property - where availale
        /// /// </summary>
        public int AdWidth
        {
            get { return (int)GetValue(AdWidthProperty); }
            set { SetValue(AdWidthProperty, value); }
        }

        public static readonly DependencyProperty AdWidthProperty = DependencyProperty.Register("AdWidth", typeof(int), typeof(AdRotatorControl), new PropertyMetadata(480, AdWidthChanged));

        private static void AdWidthChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sender = d as AdRotatorControl;
            if (sender != null)
            {
                sender.AdWidthChanged(e);
            }
        }

        private void AdWidthChanged(DependencyPropertyChangedEventArgs e)
        {
        }

        #endregion

        #region AdHeight

        /// <summary>
        /// Sets the Ad Controls Ad Height property - where availale
        /// </summary>
        public int AdHeight
        {
            get { return (int)GetValue(AdHeightProperty); }
            set { SetValue(AdHeightProperty, value); }
        }

        public static readonly DependencyProperty AdHeightProperty = DependencyProperty.Register("AdHeight", typeof(int), typeof(AdRotatorControl), new PropertyMetadata(80, AdHeightChanged));

        private static void AdHeightChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sender = d as AdRotatorControl;
            if (sender != null)
            {
                sender.AdHeightChanged(e);
            }
        }

        private void AdHeightChanged(DependencyPropertyChangedEventArgs e)
        {
        }

        #endregion

        #region IsTest

        /// <summary>
        /// When set to true the control runs Ad Providers in "Test" mode if available
        /// </summary>
        public bool IsTest
        {
            get { return (bool)GetValue(IsTestProperty); }
            set { SetValue(IsTestProperty, value); }
        }

        public static readonly DependencyProperty IsTestProperty = DependencyProperty.Register("IsTest", typeof(bool), typeof(AdRotatorControl), new PropertyMetadata(false, IsTestChanged));

        private static void IsTestChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sender = d as AdRotatorControl;
            if (sender != null)
            {
                sender.AdWidthChanged(e);
            }
        }

        private void IsTestChanged(DependencyPropertyChangedEventArgs e)
        {
        }

        #endregion

        #region PubCenter Enabled Fix
        public bool ApplyIsEnabledFix
        {
            get { return (bool)GetValue(ApplyIsEnabledFixProperty); }
            set { SetValue(ApplyIsEnabledFixProperty, value); }
        }

        // Using a DependencyProperty as the backing store for ApplyIsEnabledFix.  This enables animation, styling, binding, etc...
        public static readonly DependencyProperty ApplyIsEnabledFixProperty =
            DependencyProperty.Register("ApplyIsEnabledFix", typeof(bool), typeof(AdRotatorControl), new PropertyMetadata(false));
        #endregion

        public AdRotatorControl()
        {
            this.DefaultStyleKey = typeof(AdRotatorControl);
            this.Loaded += new RoutedEventHandler(AdRotatorControl_Loaded);
        }

        void AdRotatorControl_Loaded(object sender, RoutedEventArgs e)
        {
            if (IsInDesignMode)
            {
                LayoutRoot.Children.Add(new TextBlock() { Text = "AdRotator in design mode, No ads will be displayed", VerticalAlignment = VerticalAlignment.Center });
            }

            //Assume Network is available until we know for sure it isn't
            IsNetworkAvailable = true; 
            ConnectionProfile connectionProfile = NetworkInformation.GetInternetConnectionProfile();
            if (connectionProfile == null || connectionProfile.GetNetworkConnectivityLevel() != NetworkConnectivityLevel.InternetAccess)
            {
                IsNetworkAvailable = false;
            }
            _loaded = true;

            InitialiseSlidingAnimations();
            
        }

        /// <summary>
        /// Displays a new ad
        /// </summary>
        /// <param name="selectNextAdType">If set to true, selects the next ad type in order, otherwise chooses 
        ///     a random one that hasn't had issues loading previously</param>
        /// <returns></returns>
        public async Task<string> Invalidate(bool selectNextAdType = false)
        {
            string adTypeName = "";

            if (!_loaded || !_initialised)
            {
                if (!_initialised)
                {
                    await LoadAdSettings();
                }
                else
                {
                    OnLog("Control tested before loaded");
                    return "";
                }

            }
            if (!IsAdRotatorEnabled)
            {
                OnLog("Ad control disabled");
                Visibility = Visibility.Collapsed;
                return adTypeName;
            }
            else
            {
                OnLog("Ads are enabled for display");
                Visibility = Visibility.Visible;
            }
            if (LayoutRoot == null)
            {
                OnLog("No layout to attach to");
                return adTypeName;
            }

            if (SlidingAdDirection != SlideDirection.None && !_slidingAdTimerStarted)
            {
                _slidingAdTimerStarted = true;
                ResetSlidingAdTimer(SlidingAdDisplaySeconds);
            }

            RemoveEventHandlersFromAdControl();

            LayoutRoot.Children.Clear();
            AdType adType;
            if (IsNetworkAvailable)
            {
                if (selectNextAdType)
                {
                    adType = CurrentAdType++;
                    if (adType >= AdType.None)
                    {
                        adType = AdType.None;
                    }
                }
                else
                {
                    adType = GetNextAdType();
                }
            }
            else
            {
                CurrentCulture = CurrentCulture == null ? GetAdDescriptorBasedOnUICulture() : CurrentCulture;
                if (CurrentCulture == null)
                {
                    //Failed to get any add configuration, control disabled
                    adType = AdType.None;
                }
                else
                {
                    var defaultHouseAd = CurrentCulture.AdProbabilities.FirstOrDefault(x => x.AdType == AdType.DefaultHouseAd);
                    if (defaultHouseAd != null)
                    {
                        adType = AdType.DefaultHouseAd;
                    }
                    else
                    {
                        adType = AdType.None;
                    }
                }
            }
            OnLog(string.Format("Ads being requested for: {0}", adType.ToString()));
            switch (adType)
            {
                case AdType.PubCenter:
                    if (_currentAdControl as Microsoft.Advertising.WinRT.UI.AdControl == null)
                        _currentAdControl = CreatePubCentertAdControl();
                    break;
                case AdType.AdDuplex:
                    if (_currentAdControl as AdDuplex.Controls.AdControl == null)
                        _currentAdControl = CreateAdDuplexControl();
                    break;
                case AdType.InMobi:
                    if (_currentAdControl as IMAdView == null)
                        _currentAdControl = CreateInMobiControl();
                    break;
                case AdType.DefaultHouseAd:
                    _currentAdControl = await CreateDefaultHouseAdControl();
                    break;
                default:
                    break;

            }


            if ((_currentAdControl == null || _currentAdControl as NoneProvider != null) && _adsAvailable)
            {
                OnAdLoadFailed(adType);
                return adTypeName;
            }

            if (_currentAdControl == null || CurrentAdType == AdType.None || _currentAdControl as NoneProvider != null)
            {
                IsAdRotatorEnabled = false;
                Visibility = Visibility.Collapsed;
                OnLog("No ads available, nothing to show");
            }
            else
            {
                Visibility = Visibility.Visible;
                AddEventHandlersToAdControl();
                LayoutRoot.Children.Add((FrameworkElement)_currentAdControl);
                adTypeName = adType.ToString();
                OnLog(string.Format("Ads being served for: {0}", adType.ToString()));
            }

            return adTypeName;
        }

        /// <summary>
        /// Generates what the next ad type to display should be
        /// </summary>
        /// <returns></returns>
        private AdType GetNextAdType()
        {
            if (CurrentCulture == null)
            {
                CurrentCulture = GetAdDescriptorBasedOnUICulture();
            }

            if (CurrentCulture == null)
            {
                return DefaultAdType;
            }

            var validDescriptors = CurrentCulture.AdProbabilities
                .Where(x => !_failedAdTypes.Contains(x.AdType)
                            && x.ProbabilityValue > 0
                            && IsAdTypeValid(x.AdType))
                .ToList();
            var defaultHouseAd = CurrentCulture.AdProbabilities.FirstOrDefault(x => x.AdType == AdType.DefaultHouseAd && !_failedAdTypes.Contains(x.AdType));
            if (validDescriptors.Count > 0)
            {
                var totalValueBetweenValidAds = validDescriptors.Sum(x => x.ProbabilityValue);
                var randomValue = _rnd.NextDouble() * totalValueBetweenValidAds;
                double totalCounter = 0;
                foreach (var probabilityDescriptor in validDescriptors)
                {
                    totalCounter += probabilityDescriptor.ProbabilityValue;
                    if (randomValue < totalCounter)
                    {
                        _adsAvailable = true;
                        return probabilityDescriptor.AdType;
                    }
                }
            }
            if (defaultHouseAd != null)
            {
                _adsAvailable = true;
                return AdType.DefaultHouseAd;
            }
            _adsAvailable = false;
            return !_failedAdTypes.Contains(DefaultAdType) ? DefaultAdType : AdType.None;
        }

        /// <summary>
        /// Called when the settings have been loaded. Clears all failed ad types and invalidates the control
        /// </summary>
        private async void Init()
        {
            _failedAdTypes.Clear();
            await Invalidate();
        }

        #region Ad Event Handlers
        private void AddEventHandlersToAdControl()
        {
            var pubCenterAd = _currentAdControl as Microsoft.Advertising.WinRT.UI.AdControl;
            if (pubCenterAd != null)
            {
                pubCenterAd.AdRefreshed += pubCenterAd_AdRefreshed;
                pubCenterAd.ErrorOccurred += pubCenterAd_ErrorOccurred;
            }

            var inMobiAd = _currentAdControl as IMAdView;
            if (inMobiAd != null)
            {
                inMobiAd.OnAdRequestFailed += AdView_AdRequestFailed;
                inMobiAd.OnAdRequestLoaded += AdView_AdRequestLoaded;
            }
        }

        private void RemoveEventHandlersFromAdControl()
        {
            var pubCenterAd = _currentAdControl as Microsoft.Advertising.WinRT.UI.AdControl;
            if (pubCenterAd != null)
            {
                pubCenterAd.AdRefreshed -= pubCenterAd_AdRefreshed;
                pubCenterAd.ErrorOccurred -= pubCenterAd_ErrorOccurred;
            }
            var DefaultHouseAd = _currentAdControl as DefaultHouseAd;
            if (DefaultHouseAd != null)
            {
                DefaultHouseAd.AdClicked -= defaultHouseAd_AdClicked;
                DefaultHouseAd.AdLoaded -= defaultHouseAd_AdLoaded;
                DefaultHouseAd.AdLoadingFailed -= defaultHouseAd_AdLoadingFailed;
            }

            var inMobiAd = _currentAdControl as IMAdView;
            if (inMobiAd != null)
            {
                inMobiAd.OnAdRequestFailed -= AdView_AdRequestFailed;
                inMobiAd.OnAdRequestLoaded -= AdView_AdRequestLoaded;
            }
        }

        private AdCultureDescriptor GetAdDescriptorBasedOnUICulture()
        {
            if (_settings == null || _settings.CultureDescriptors == null)
            {
                return null;
            }
            var cultureLongName = System.Globalization.CultureInfo.CurrentUICulture.Name;
            if (String.IsNullOrEmpty(cultureLongName))
            {
                cultureLongName = AdSettings.DEFAULT_CULTURE;
            }
            var cultureShortName = cultureLongName.Substring(0, 2);
            var descriptor = _settings.CultureDescriptors.Where(x => x.CultureName == cultureLongName).FirstOrDefault();
            if (descriptor != null)
            {
                return descriptor;
            }
            var sameLanguageDescriptor = _settings.CultureDescriptors.Where(x => x.CultureName.StartsWith(cultureShortName)).FirstOrDefault();
            if (sameLanguageDescriptor != null)
            {
                return sameLanguageDescriptor;
            }
            var defaultDescriptor = _settings.CultureDescriptors.Where(x => x.CultureName == AdSettings.DEFAULT_CULTURE).FirstOrDefault();
            if (defaultDescriptor != null)
            {
                return defaultDescriptor;
            }
            return null;
        }

        private void RemoveAdFromFailedAds(AdType adType)
        {
            if (_failedAdTypes.Contains(adType))
            {
                _failedAdTypes.Remove(adType);
            }
        }

        /// <summary>
        /// Called when <paramref name="adType"/> has failed to load
        /// </summary>
        /// <param name="adType"></param>
        private async void OnAdLoadFailed(AdType adType)
        {
            OnLog(string.Format("Ads failed request for: {0}", adType.ToString()));
            if (!_failedAdTypes.Contains(adType))
            {
                _failedAdTypes.Add(adType);
            }

            await Invalidate(); 
        }

        /// <summary>
        /// Called when <paramref name="adType"/> has succeeded to load
        /// </summary>
        /// <param name="adType"></param>
        private void OnAdLoadSucceeded(AdType adType)
        {
            OnLog(string.Format("Ads being successfully served for: {0}", adType.ToString()));
            if (_failedAdTypes.Contains(adType))
            {
                _failedAdTypes.Remove(adType);
            }
        }

        #endregion

        #region Ad Settings Retrieval
        private string GetAppID(AdType adType)
        {
            return CurrentCulture.AdProbabilities
                    .Where(x => x.AdType == adType)
                    .First().AppID;
        }

        private string GetSecondaryID(AdType adType)
        {
            return CurrentCulture.AdProbabilities
                    .Where(x => x.AdType == adType)
                    .First().SecondaryID;
        }

        #endregion

        #region Save and Load
        /// <summary>
        /// Loads the ad settings object either from isolated storage or from the resource path defined in DefaultSettingsFileUri.
        /// </summary>
        /// <returns></returns>
        private async Task LoadAdSettings()
        {
            //Conditions right for getting remote settings - try
            if (!_remoteAdSettingsFetched && !String.IsNullOrEmpty(_settingsURL) && IsNetworkAvailable)
            {
                _settings = await FetchAdSettingsFile(null);
            }

            //if remote failed or no network available
            if (_settings == null && !String.IsNullOrEmpty(_settingsURL))
            {
                try
                {
                    _settings = (AdSettings)await Win8StorageHelper.LoadData(SETTINGS_FILE_NAME, typeof(AdSettings));
                }
                catch { }
            }

            //if still no settings get from the project
            if (_settings == null && !string.IsNullOrEmpty(DefaultSettingsFileUri))
            {
                _settings = (AdSettings)await Win8StorageHelper.LoadData(DefaultSettingsFileUri, Windows.ApplicationModel.Package.Current.InstalledLocation, typeof(AdSettings));
            }

            //If still failed, disable AdRotator else all is green
            if (_settings == null)
            {
                OnLog("Ad control disabled no settings available");
                Visibility = Visibility.Collapsed;
                IsEnabled = false;
            }
            else
            {
                //Everything OK, continue loading
                _initialised = true;
                 //await Invalidate();
            }

        }

        /// <summary>
        /// Fetches the ad settings file from the address specified
        /// </summary>
        public async Task<AdSettings> FetchAdSettingsFile(Object stateInfo)
        {
            try
            {
                var settingsStream = await AdRotator.Networking.Network.GetStreamFromURL(_settingsURL);
                var xs = new XmlSerializer(typeof(AdSettings));
                var settings = (AdSettings)xs.Deserialize(settingsStream);
                var saved = await SaveAdSettings(settings);
                _initialised = true;
                _remoteAdSettingsFetched = true;
                return settings;
            }
            catch
            {
                _remoteAdSettingsFetched = true;
                return null;
            }

        }


        /// <summary>
        /// Saves the passed settings file to isolated storage
        /// </summary>
        /// <param name="settings"></param>
        private async static Task<bool> SaveAdSettings(AdSettings settings)
        {
            try
            {
                return await Win8StorageHelper.SaveData(SETTINGS_FILE_NAME, settings);
            }
            catch
            {
                return false;
            }
        }

        #endregion

        #region Specific ad controls

        #region DefaultHouseAd


        private async Task<object> CreateDefaultHouseAdControl()
        {
            FrameworkElement defaultHouseAd;

            defaultHouseAd = new DefaultHouseAd();
            ((DefaultHouseAd)defaultHouseAd).AdClicked += new DefaultHouseAd.OnAdClicked(defaultHouseAd_AdClicked);
            ((DefaultHouseAd)defaultHouseAd).AdLoaded += new DefaultHouseAd.OnAdLoaded(defaultHouseAd_AdLoaded);
            ((DefaultHouseAd)defaultHouseAd).AdLoadingFailed += new DefaultHouseAd.OnAdFailed(defaultHouseAd_AdLoadingFailed);
            defaultHouseAd = await ((DefaultHouseAd)defaultHouseAd).Initialise(DefaultHouseAdBody, DefaultHouseAdURI);
            defaultHouseAd.Width = AdWidth;
            defaultHouseAd.Height = AdHeight;

            if (defaultHouseAd == null)
            {
                OnLog(string.Format("No Default HouseAd Config available"));
                return new NoneProvider();
            }
            else
            {
                OnLog(string.Format("DefaultHouseAd Created"));
                return defaultHouseAd;
            }

        }

        void defaultHouseAd_AdLoadingFailed(object sender, EventArgs e)
        {
            OnLog(string.Format("Failed loading Default HouseAd"));
            OnAdLoadFailed(AdType.DefaultHouseAd);
        }

        void defaultHouseAd_AdLoaded(object sender, EventArgs e)
        {
            OnAdLoadSucceeded(AdType.DefaultHouseAd);
        }

        void defaultHouseAd_AdClicked(object sender, EventArgs e)
        {
            OnLog(string.Format("DefaultHouseAd Clicked"));
            if (DefaultHouseAdClick != null)
            {
                DefaultHouseAdClick();
            }
        }

        #endregion

        #region Pubcenter
        //Disabled for now - Don't work!!
        private object CreatePubCentertAdControl()
        {
            try
            {
                var pubCenterAdControl = new Microsoft.Advertising.WinRT.UI.AdControl();
                pubCenterAdControl.ApplicationId = PubCenterAppId;
                pubCenterAdControl.AdUnitId = PubCenterAdUnitId;
                pubCenterAdControl.IsAutoRefreshEnabled = true; // isAutoRefreshEnabled
                pubCenterAdControl.Width = AdWidth;
                pubCenterAdControl.Height = AdHeight;
                pubCenterAdControl.IsEnabled = !ApplyIsEnabledFix;
                return pubCenterAdControl;
            }
            catch (Exception)
            {
                return new NoneProvider();
            }

        }

        void pubCenterAd_ErrorOccurred(object sender, Microsoft.Advertising.WinRT.UI.AdErrorEventArgs e)
        {
            var message = e == null ? string.Empty : e.Error.Message.ToString();
            OnLog(string.Format("PubCenter Failed: {0}", message));
            OnAdLoadFailed(AdType.PubCenter);
        }

        void pubCenterAd_AdRefreshed(object sender, RoutedEventArgs e)
        {
            var message = e == null ? string.Empty : e.ToString();
            OnLog(string.Format("pubCenter Success: {0}", message));
            OnAdLoadSucceeded(AdType.PubCenter);
        }

        public void Suspend()
        {
            Microsoft.Advertising.WinRT.UI.AdControl msAd =
                this._currentAdControl as Microsoft.Advertising.WinRT.UI.AdControl;
            if (msAd != null)
            {
                msAd.Suspend();
            }
        }

        public void Resume()
        {
            Microsoft.Advertising.WinRT.UI.AdControl msAd =
                this._currentAdControl as Microsoft.Advertising.WinRT.UI.AdControl;
            if (msAd != null)
            {
                msAd.Resume();
            }
        }

        #endregion

        #region AdDuplex
        private object CreateAdDuplexControl()
        {
            try
            {
                var adDuplexAd = new AdDuplex.Controls.AdControl();
                adDuplexAd.AppId = AdDuplexAppId;
                adDuplexAd.Size = string.Format("{0}x{1}", AdWidth, AdHeight);
                adDuplexAd.Width = AdWidth;
                adDuplexAd.Height = AdHeight;
                adDuplexAd.IsTest = IsTest;
                return adDuplexAd;
            }
            catch (Exception)
            {
                return new NoneProvider();
            }
        }

        #endregion

        #region InMobi
        private object CreateInMobiControl()
        {
            try
            {
                IMAdView AdView = new IMAdView();

                AdView.AdSize = IMAdView.INMOBI_AD_UNIT_480x75;

                //Subscribe for IMAdView events
                //AdView.OnDismissAdScreen += new EventHandler(AdView_DismissFullAdScreen);
                //AdView.OnLeaveApplication += new EventHandler(AdView_LeaveApplication);
                //AdView.OnShowAdScreen += new EventHandler(AdView_ShowFullAdScreen);


                //Set the AppId. Provide you AppId
                AdView.AppId = InMobiAppId;
                AdView.RefreshInterval = 20;
                AdView.AnimationType = IMAdAnimationType.NONE;
                IMAdRequest imAdRequest = new IMAdRequest();
                AdView.LoadNewAd(imAdRequest);
                return AdView;
            }
            catch (Exception)
            {
                return new NoneProvider();
            }
        }

        void AdView_AdRequestFailed(object sender, IMAdViewErrorEventArgs e)
        {
            OnLog(string.Format("InMobi Failed: {0} - {1} ", e.ErrorCode.ToString(), e.ErrorDescription.ToString()));
            OnAdLoadFailed(AdType.InMobi);
        }

        void AdView_AdRequestLoaded(object sender, EventArgs e)
        {
            OnLog(string.Format("InMobi Success: {0}", e.ToString()));
            OnAdLoadSucceeded(AdType.InMobi);
        }
        #endregion

        #endregion

        #region Animation Events
        private void SlideOutAdStoryboard_Completed(object sender, object e)
        {
            _slidingAdHidden = true;
            Invalidate();
            ResetSlidingAdTimer(SlidingAdHiddenSeconds);
        }

        private void SlideInAdStoryboard_Completed(object sender, object e)
        {
            _slidingAdHidden = false;
            ResetSlidingAdTimer(SlidingAdDisplaySeconds);
        }

        private void ResetSlidingAdTimer(int durationInSeconds)
        {
            if (IsAdRotatorEnabled)
            {
                SlidingAdTimer.Duration = new Duration(new TimeSpan(0, 0, durationInSeconds));
                SlidingAdTimer.Begin();
            }
        }

        private void SlidingAdTimer_Completed(object sender, object e)
        {
            switch (SlidingAdDirection)
            {
                case SlideDirection.Top:
                case SlideDirection.Bottom:
                    if (_slidingAdHidden)
                    {
                        SlideInUDAdStoryboard.Begin();
                    }
                    else
                    {
                        SlideOutUDAdStoryboard.Begin();
                    }
                    break;
                case SlideDirection.Left:
                case SlideDirection.Right:
                    if (_slidingAdHidden)
                    {
                        SlideInLRAdStoryboard.Begin();
                    }
                    else
                    {
                        SlideOutLRAdStoryboard.Begin();
                    }
                    break;
                default:
                    break;
            }
        }

        void InitialiseSlidingAnimations()
        {
            SlidingAdTimer = new Storyboard();
            SlidingAdTimer.Completed += SlidingAdTimer_Completed;

            DoubleAnimation SlideOutLRAdStoryboardAnimation = new DoubleAnimation();
            SlideOutLRAdStoryboardAnimation.EnableDependentAnimation = true;
            Storyboard.SetTarget(SlideOutLRAdStoryboardAnimation, LayoutRoot);
            Storyboard.SetTargetProperty(SlideOutLRAdStoryboardAnimation, "(UIElement.RenderTransform).(CompositeTransform.TranslateX)");
            SlideOutLRAdStoryboardAnimation.Completed += SlideOutAdStoryboard_Completed;

            SlideOutLRAdStoryboard = new Storyboard();
            SlideOutLRAdStoryboard.Children.Add(SlideOutLRAdStoryboardAnimation);


            DoubleAnimation SlideInLRAdStoryboardAnimation = new DoubleAnimation();
            SlideInLRAdStoryboardAnimation.EnableDependentAnimation = true;
            Storyboard.SetTarget(SlideInLRAdStoryboardAnimation, LayoutRoot);
            Storyboard.SetTargetProperty(SlideInLRAdStoryboardAnimation, "(UIElement.RenderTransform).(CompositeTransform.TranslateX)");
            SlideInLRAdStoryboardAnimation.Completed += SlideInAdStoryboard_Completed;

            SlideInLRAdStoryboard = new Storyboard();
            SlideInLRAdStoryboard.Children.Add(SlideInLRAdStoryboardAnimation);

            DoubleAnimation SlideOutUDAdStoryboardAnimation = new DoubleAnimation();
            SlideOutUDAdStoryboardAnimation.EnableDependentAnimation = true;
            Storyboard.SetTarget(SlideOutUDAdStoryboardAnimation, LayoutRoot);
            Storyboard.SetTargetProperty(SlideOutUDAdStoryboardAnimation, "(UIElement.RenderTransform).(CompositeTransform.TranslateY)");
            SlideOutUDAdStoryboardAnimation.Completed += SlideOutAdStoryboard_Completed;

            SlideOutUDAdStoryboard = new Storyboard();
            SlideOutUDAdStoryboard.Children.Add(SlideOutUDAdStoryboardAnimation);

            DoubleAnimation SlideInUDAdStoryboardAnimation = new DoubleAnimation();
            SlideInUDAdStoryboardAnimation.EnableDependentAnimation = true;
            Storyboard.SetTarget(SlideInUDAdStoryboardAnimation, LayoutRoot);
            Storyboard.SetTargetProperty(SlideInUDAdStoryboardAnimation, "(UIElement.RenderTransform).(CompositeTransform.TranslateY)");
            SlideInUDAdStoryboardAnimation.Completed += SlideInAdStoryboard_Completed;

            SlideInUDAdStoryboard = new Storyboard();
            SlideInUDAdStoryboard.Children.Add(SlideInUDAdStoryboardAnimation);

            SlidingAdDirection = SlidingAdDirection;
        }
        #endregion

        public void Dispose()
        {
            LayoutRoot.Children.Clear();
            _currentAdControl = null;
            DefaultHouseAdBody = null;
        }
    }
}
